package cn.edu.neu.simpleppt;

import com.jfoenix.controls.JFXButton;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.*;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseDragEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.*;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
import javafx.stage.FileChooser;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.io.File;
import java.util.Vector;

/**
 * 处理主窗口（paint-view.fxml）的事件
 * 图形的绘制、删除、创建页面、删除页面、打开、保存文件等操作
 */
public class PaintController {
    private Point2D startMousePoint;         // 开始绘制记录时的鼠标位置
    private Point2D startPointInPage;       // 鼠标位置在幻灯片内部的坐标
    private PPTShapes to_add;               // 要添加的元素类型
    private Node nodeToAdd;                 // 要添加的元素

    private int currentPage;                // 当前页数
    private Vector<Vector<Node>> objs;      // 用来存储所有元素的 Vector

    /**
     * 带 @FXML 表示来自 fxml 界面的控件，这些组件被定义在 fxml 文件里，使用 JAVAFX 的 SceneBuilder
     * 创建，只需要在需要的时候载入就行
     */
    @FXML
    private Pane background;        // 背景
    @FXML
    private Pane controls;          // 添加元素时显示的图层
    @FXML
    private Pane shapesLayer;       // 存储幻灯片内容的图层
    @FXML
    private VBox pages;             // ppt 列表
    @FXML
    private ScrollPane scrollPane;  // 右面显示页面的滚动窗口，每添加一页内容时，滚动到 ppt 列表末尾
    @FXML
    private JFXButton addBtn;       // 按钮：添加页面
    @FXML
    private JFXButton delBtn;       // 按钮：删除页面
    @FXML
    private JFXButton lineBtn;       // 按钮：绘制直线
    @FXML
    private JFXButton rectBtn;       // 按钮：绘制矩形
    @FXML
    private JFXButton ellipseBtn;    // 按钮：绘制椭圆
    @FXML
    private JFXButton circleBtn;       // 按钮：绘制圆形
    @FXML
    private JFXButton textBtn;       // 按钮：绘制文本
    @FXML
    private JFXButton polylineBtn;       // 按钮：绘制任意线
    @FXML
    private JFXButton openBtn;       // 按钮：打开文件
    @FXML
    private JFXButton saveBtn;       // 按钮：保存文件

    private Popup editInfoWindow;       // 窗口：显示、编辑图形

    // controller: 处理 editInfoWindow 的事件
    private EditInfoController editInfoController;

    /**
     * 当组件加载完毕时调用这个函数，可以用来初始化一些组件的状态
     */
    public void initialize() {
        // 幻灯片页面的高度 = 宽度 * 9 / 16
        background.prefHeightProperty().bind(background.widthProperty().multiply(9).divide(16));

        controls.setVisible(false);
        delBtn.setVisible(false);

        shapesLayer.prefWidthProperty().bind(background.widthProperty());
        shapesLayer.prefHeightProperty().bind(background.heightProperty());

        // 初始状态
        currentPage = 0;
        objs = new Vector<>();
        pages.getChildren().add(createPageButton());
        objs.add(new Vector<>());

        // 设置事件监听
        initDrawOpt();
        initShapeLayerEvent();

        // 载入、初始化编辑窗口
        initEditInfoWindow();

        //  载入图标
        initIcons();
    }

    /**
     * 载入图标
     */
    private void initIcons() {
        JFXButton [] btns = new JFXButton[] {
                openBtn, saveBtn, lineBtn, rectBtn, ellipseBtn, circleBtn, textBtn, polylineBtn, addBtn, delBtn
        };
        String[] icons = new String[] {
                "imgs/open.png","imgs/save.png",
                "imgs/line.png","imgs/rect.png", "imgs/ellipse.png",
                "imgs/circle.png", "imgs/text.png", "imgs/polyline.png",
                "imgs/add.png", "imgs/remove.png"
        };

        for (int i = 0; i < btns.length; i++) {
            try {
                ImageView img = new ImageView(PaintController.class.getResource(icons[i]).toExternalForm());
                img.setFitWidth(35);
                img.setFitHeight(35);
                btns[i].setGraphic(img);
                btns[i].setPadding(new Insets(4));
            } catch (Exception e) {
                btns[i].setText(icons[i]);
            }
        }

        ((ImageView) addBtn.getGraphic()).setFitHeight(24);
        ((ImageView) addBtn.getGraphic()).setFitWidth(24);
        ((ImageView) delBtn.getGraphic()).setFitWidth(24);
        ((ImageView) delBtn.getGraphic()).setFitHeight(24);
    }

    /**
     * 开始绘制时要监听鼠标动作
     */
    private void initDrawOpt() {
        controls.addEventFilter(MouseDragEvent.MOUSE_PRESSED, this::onAddShape);
        controls.addEventFilter(MouseDragEvent.MOUSE_DRAGGED, this::onChangeShape);
        controls.addEventFilter(MouseEvent.MOUSE_RELEASED, e -> updateThumbnail());
        shapesLayer.addEventFilter(MouseEvent.MOUSE_RELEASED, e->updateThumbnail());
        controls.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
            if (keyEvent.getCode() == KeyCode.ESCAPE) {
                controls.setCursor(Cursor.DEFAULT);
                controls.setVisible(false);
            }
        });
    }


    /**
     * 初始化编辑窗口
     */
    private void initEditInfoWindow() {
        try {
            //  载入界面文件 edit-info.fxml
            FXMLLoader loader = new FXMLLoader(PaintController.class.getResource("edit-info.fxml"));

            // 创建窗口
            editInfoWindow = new Popup();
            editInfoWindow.getContent().add(loader.load());
            editInfoWindow.setWidth(100);
            editInfoWindow.setHeight(200);

            // 将 controller 暴露出来，以便之后使用
            editInfoController = loader.getController();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 当点击页面上的元素时，调用这个函数
     * 在页面上给元素添加控制框，这样子就可以对移动元素位置、或者改变大小
     */
    private void onNodeClicked(MouseEvent e) {
        // 获取被点击的元素
        Node source = (Node) e.getSource();
        // 删除其他元素的控制框（单选）
        for (Node n: shapesLayer.getChildren()) {
            ControlPoints points = (ControlPoints) n.getUserData();
            if (points != null) {
                shapesLayer.getChildren().remove(points.getControl());
                n.setUserData(null);
                break;
            }
        }

        // 如果当前元素没有控制框，就给它绘制一个
        if (source.getUserData() == null) {
            ControlPoints controlPoints = new ControlPoints(source);
            source.setUserData(controlPoints);

            // 显示控制框
            shapesLayer.getChildren().add(controlPoints.getControl());
        }

        // 显示编辑窗口
        showEditInfoWindow();
        editInfoController.showSettingsToEdit(source);
    }

    /**
     * 在添加图形时，开始拖拽鼠标，按下鼠标左键时调用
     * 用来创建要添加的图形，并从 editInfoWindow 读取配置
     */
    private void onAddShape(MouseEvent mouseEvent) {
        // 记录鼠标位置，用来确定图形的位置
        startMousePoint = new Point2D(mouseEvent.getX(), mouseEvent.getY());
        startPointInPage = startMousePoint.subtract(background.getLayoutX(), background.getLayoutY());

        // 创建图形
        nodeToAdd = createShape();
        if (nodeToAdd == null) {
            return;
        }

        nodeToAdd.setPickOnBounds(true);

        // 绘制图形时关闭编辑窗口
        closeEditInfoWindow();

        // 监听鼠标单击事件
        nodeToAdd.setOnMouseClicked(this::onNodeClicked);

        // 从 editInfoController 里读取配置，创建图形
        ShapeSettings settings = editInfoController.getSettingsToAdd();
        if (nodeToAdd instanceof Shape) {
            Shape shape = (Shape) nodeToAdd;
            shape.setFill(settings.background);
            shape.setStroke(settings.foreground);
            if (settings.lineGaps > 0) {
                shape.getStrokeDashArray().add(settings.lineGaps);
            }
            shape.setStrokeWidth(settings.lineWidth);
            shape.setLayoutX(startPointInPage.getX());
            shape.setLayoutY(startPointInPage.getY());
        }
        if (nodeToAdd instanceof Label) {
            Label label = (Label) nodeToAdd;
            label.setFont(Font.font(settings.fontSize));
            label.setText(settings.text);
            label.setTextFill(settings.fontColor);
            label.setLayoutX(startPointInPage.getX());
            label.setLayoutY(startPointInPage.getY());
            if (settings.textAlign == TextAlignment.LEFT) {
                label.setAlignment(Pos.TOP_LEFT);
            } else if (settings.textAlign == TextAlignment.CENTER) {
                label.setAlignment(Pos.TOP_CENTER);
            } else {
                label.setAlignment(Pos.TOP_RIGHT);
            }
            label.setTextAlignment(settings.textAlign);
            label.setWrapText(true);
        }

        // 将创建的元素添加到界面上
        shapesLayer.getChildren().add(nodeToAdd);
    }

    /**
     * 创建图形、每当拖拽鼠标时调用这个函数，主要用来让元素的形状随鼠标位置改变而改变
     */
    private void onChangeShape(MouseEvent mouseEvent) {
        // 获取鼠标位置，计算与初始位置的差
        Point2D delta = new Point2D(mouseEvent.getX(), mouseEvent.getY()).subtract(startMousePoint);

        double centerX = delta.getX() / 2;
        double centerY = delta.getY() / 2;

        // 针对不同的类型分别进行处理，更新元素的形状
        if (nodeToAdd instanceof Ellipse) {
            Ellipse circle = (Ellipse) nodeToAdd;

            // 圆
            if (to_add == PPTShapes.Circle) {
                double r = Math.min(Math.abs(centerX), Math.abs(centerY));
                circle.setCenterX(r);
                circle.setCenterY(r);
                circle.setRadiusX(r);
                circle.setRadiusY(r);
                return;
            }

            // 椭圆
            Ellipse ellipse = (Ellipse) nodeToAdd;
            ellipse.setCenterX(centerX);
            ellipse.setCenterY(centerY);
            ellipse.setRadiusX(Math.abs(delta.getX() / 2));
            ellipse.setRadiusY(Math.abs(delta.getY() / 2));
            return;
        }

        // 矩形
        if (nodeToAdd instanceof Rectangle) {
            Rectangle rectangle = (Rectangle) nodeToAdd;
            rectangle.setWidth(delta.getX());
            rectangle.setHeight(delta.getY());
            return;
        }

        if (nodeToAdd instanceof Label) {
            return;
        }

        // 直线
        if (nodeToAdd instanceof Line) {
            Line line = (Line) nodeToAdd;
            line.setEndX(delta.getX());
            line.setEndY(delta.getY());
            return;
        }

        // 任意线
        if (nodeToAdd instanceof Polyline) {
            Polyline polyline = (Polyline) nodeToAdd;
            polyline.getPoints().addAll(delta.getX(), delta.getY());
        }
    }

    /**
     * 获取当前页面的截图，更新对应按钮的内容
     */
    private void updateThumbnail() {
        ImageView imgV = (ImageView) ((JFXButton) pages.getChildren().get(currentPage)).getGraphic();
        Image snapshot = background.snapshot(new SnapshotParameters(), null);

        // 计算剪裁范围
        Bounds inLocal = background.getBoundsInLocal();
        Rectangle2D clip =  new Rectangle2D(Math.abs(inLocal.getMinX()), Math.abs(inLocal.getMinY()),
                background.widthProperty().get(), background.heightProperty().get());
        imgV.setViewport(clip);

        // 绘制截图
        imgV.setImage(snapshot);
    }

    /**
     * 监听绘制图层的事件
     */
    private void initShapeLayerEvent() {

        // 点击到空白区域时，删除所有元素的控制框
        shapesLayer.setOnMouseClicked(e -> {
            if (e.getTarget() == e.getSource()) {
                removeControls();
                updateThumbnail();

                // 关闭编辑窗口
                closeEditInfoWindow();
            }
            // 获取焦点
            shapesLayer.requestFocus();
        });

        // 选中内容后，单击 delete 键删除元素
        shapesLayer.setOnKeyPressed(e -> {
            if (e.getCode() == KeyCode.DELETE || e.getCode() == KeyCode.BACK_SPACE) {
                Vector<Node> to_remove = new Vector<>();
                for (Node n: shapesLayer.getChildren()) {
                    ControlPoints points = (ControlPoints) n.getUserData();
                    if (points != null) {
                        to_remove.add(points.getControl());
                        to_remove.add(n);
                        n.setUserData(null);
                    }
                }
                shapesLayer.getChildren().removeAll(to_remove);
                updateThumbnail();
                closeEditInfoWindow();
            }
        });
    }

    /**
     * 删除页面上的控制框
     */
    private void removeControls() {
        Vector<Node> to_remove = new Vector<>();

        for (Node n: shapesLayer.getChildren()) {
            ControlPoints points = (ControlPoints) n.getUserData();
            if (points != null) {
                to_remove.add(points.getControl());
                n.setUserData(null);
            }
        }

        shapesLayer.getChildren().removeAll(to_remove);
    }

    /**
     * 创建元素
     */
    private Node createShape() {
        switch (to_add) {
            case Circle:
            case Ellipse:
                return new Ellipse();
            case Line: return new Line();
            case Rectangle:return new Rectangle();
            case Label:return new Label();
            case PolyLine: return new Polyline();
        }
        return null;
    }

    // 当点击左边工具栏的按钮时，显示编辑窗口
    private void startDrawShapes(PPTShapes to_draw) {
        controls.setVisible(true);
        controls.setCursor(Cursor.CROSSHAIR);
        controls.requestFocus();
        to_add = to_draw;

        editInfoController.showSettingsToAdd(to_draw);
        showEditInfoWindow();
    }

    // 切换幻灯片
    private void selectPage(ActionEvent e) {
        int toShowPage = pages.getChildren().indexOf((JFXButton) e.getSource());
        if (controls.isVisible()) {
            controls.setVisible(false);
        }
        if (toShowPage != currentPage) {
            // 将原来页面的所有内容存储到Vec中
            Vector<Node> toStore = objs.get(currentPage);
            toStore.clear();
            toStore.addAll(shapesLayer.getChildren());
            // 清空画布
            shapesLayer.getChildren().clear();
            (pages.getChildren().get(currentPage)).setStyle("");

            // 重新添加新页面的内容
            currentPage = toShowPage;
            shapesLayer.getChildren().addAll(objs.get(toShowPage));
            pages.getChildren().get(currentPage).setStyle("-fx-border-width: 3px; -fx-border-color: rgba(72,72,72,0.56)");
        }
    }

    /**
     * 创建幻灯片按钮
     * @return JFXButton
     */
    private JFXButton createPageButton() {
        JFXButton button = new JFXButton();
        final double width = 250;
        button.setPrefWidth(width);
        button.setPrefHeight(width * 9 / 16);
        button.setPadding(new Insets(0));
        button.setButtonType(JFXButton.ButtonType.RAISED);

        // 默认显示空的幻灯片
        ImageView view = new ImageView(background.snapshot(new SnapshotParameters(), null));
        view.setFitHeight(width * 9 / 16);
        view.setFitWidth(width);
        button.setGraphic(view);

        button.setStyle("-fx-border-width: 3px; -fx-border-color: rgba(72,72,72,0.56)");

        button.setOnAction(this::selectPage);
        return button;
    }

    /**
     * 这些函数在 fxml 中定义，用来绘制图形
     */
    @FXML
    protected void startDrawCircle() {
        startDrawShapes(PPTShapes.Circle);
    }
    @FXML
    protected void startDrawLine() {
        startDrawShapes(PPTShapes.Line);
    }
    @FXML
    protected void startDrawEllipse() {
        startDrawShapes(PPTShapes.Ellipse);
    }
    @FXML
    protected void startDrawRectangle() {
        startDrawShapes(PPTShapes.Rectangle);
    }
    @FXML
    protected void startDrawPath() {
        startDrawShapes(PPTShapes.PolyLine);
    }
    @FXML
    protected void startDrawText() {
        startDrawShapes(PPTShapes.Label);
    }

    /**
     * 添加幻灯片
     */
    @FXML
    protected void onAddBtnAction() {
        objs.add(new Vector<>());

        if (controls.isVisible()) {
            controls.setVisible(false);
        }

        // 将当前页面的所有内容存储到Vec中
        Vector<Node> toStore = objs.get(currentPage);
        toStore.clear();
        toStore.addAll(shapesLayer.getChildren());
        // 清空画布
        shapesLayer.getChildren().clear();
        (pages.getChildren().get(currentPage)).setStyle("");

        // 添加新的按钮
        pages.getChildren().add(createPageButton());
        this.currentPage = pages.getChildren().size() - 1;
        updateThumbnail();

        scrollPane.setVvalue(scrollPane.getVmax());
        delBtn.setVisible(true);
    }

    /**
     * 删除幻灯片
     */
    @FXML
    protected void onDeleteBtnAction() {
        // 删除当前页面的内容
        int toDelete = currentPage;
        objs.remove(toDelete);

        // 如果当前的 current 刚好为上一个元素的内容
        if (currentPage == pages.getChildren().size() - 1) {
            currentPage = currentPage - 1;
        }
        pages.getChildren().remove(toDelete);
        if (pages.getChildren().size() == 1) {
            delBtn.setVisible(false);
        }

        // 清空画布
        shapesLayer.getChildren().clear();
        shapesLayer.getChildren().addAll(objs.get(currentPage));
        pages.getChildren().get(currentPage).setStyle("-fx-border-width: 3px; -fx-border-color: rgba(72,72,72,0.56)");
    }

    // 关闭编辑窗口
    public void closeEditInfoWindow() {
        editInfoWindow.hide();
    }

    // 显示编辑窗口
    public void showEditInfoWindow() {
        Window mainWindow = shapesLayer.getScene().getWindow();

        // 记住上一次打开的位置
        if (editInfoController.isMoved) {
            editInfoWindow.show(shapesLayer.getScene().getWindow());
        } else {
            // 初始位置
            editInfoWindow.show(shapesLayer.getScene().getWindow(),
                    mainWindow.getX() + mainWindow.getWidth() - 400,
                    mainWindow.getY() + mainWindow.getHeight() - 400);
        }
    }

    /**
     * 保存文件
     */
    @FXML
    protected void saveFile() {
        // 保存当前页的状态
        removeControls();
        Vector<Node> toStore = objs.get(currentPage);
        toStore.clear();
        toStore.addAll(shapesLayer.getChildren());

        // 打开对话框
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("保存文件");
        fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("JSON 文件", "*.json"));
        File file = fileChooser.showSaveDialog(shapesLayer.getScene().getWindow());
        if (file != null) {
            try {
                // 如果没有输入后缀，则自动补全后缀
                String path;
                if (!file.getAbsolutePath().endsWith(".json")) {
                    path = file.getAbsolutePath() + ".json";
                } else {
                    path = file.getAbsolutePath();
                }
                PPTFileIO.write(path, objs);
                Stage win = (Stage) shapesLayer.getScene().getWindow();

                // 更新标题栏的内容
                win.setTitle("[已保存] " + path);
            } catch (Exception e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
    }

    /**
     * 打开文件
     */
    @FXML
    protected void openFile() {
        // 打开对话框
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("打开文件");
        fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("JSON 文件", "*.json"));
        File file = fileChooser.showOpenDialog(shapesLayer.getScene().getWindow());
        if (file == null) {
            return;
        }

        // 获取文件路径
        String path = file.getAbsolutePath();

        // 读取文件内容
        Vector<Vector<Node>> readObjs;
        try {
            readObjs = PPTFileIO.read(path);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        // 如果成功读取到文件内容，并成功转换
        if (readObjs.size() > 0) {
            objs = readObjs;

            // 清空画板上的图形
            shapesLayer.getChildren().clear();
            pages.getChildren().clear();

            // 创建幻灯片的缩略图
            for (int i = 0; i < objs.size(); i++) {
                currentPage = i;
                shapesLayer.getChildren().clear();
                shapesLayer.getChildren().addAll(objs.get(i));
                for (Node n: objs.get(i)) {
                    n.setOnMouseClicked(this::onNodeClicked);
                }
                pages.getChildren().add(createPageButton());
                pages.getChildren().get(currentPage).setStyle(null);
                updateThumbnail();
            }

            // 将内容设置成第一个幻灯片的内容
            currentPage = 0;
            shapesLayer.getChildren().clear();
            shapesLayer.getChildren().addAll(objs.get(currentPage));
            pages.getChildren().get(currentPage).setStyle("-fx-border-width: 3px; -fx-border-color: rgba(72,72,72,0.56)");

            // 更改标题
            ((Stage) shapesLayer.getScene().getWindow()).setTitle(path);
        }
    }
}